package nasp.labos.drugi.GA;

import java.awt.EventQueue;

import javax.naming.ldap.Rdn;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.JLabel;
import java.awt.GridBagConstraints;
import javax.swing.JRadioButton;
import java.awt.Insets;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.JProgressBar;
import javax.swing.ButtonGroup;
import javax.swing.SwingWorker;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;
import javax.swing.BoxLayout;
import javax.swing.JTextPane;
import javax.swing.JTextArea;
import com.swtdesigner.FocusTraversalOnArray;
import java.awt.Component;
import javax.swing.JScrollPane;

public class GUI implements PropertyChangeListener {

	private JFrame frame;
	private JTextField textField;
	private JTextField txtPopsize;
	private JTextField txtMaxiterations;
	private JTextField txtPmutation;
	private JTextField txtUpperlimit;
	private JTextField txtLowerlimit;
	private JTextField txtPrecision;
	private JTextField txtNumbits;
	private JTextField txtDimension;
	private boolean simple = true;
	private final ButtonGroup buttonGroup = new ButtonGroup();
	private JRadioButton rdbtnF3;
	private JRadioButton rdbtnF4;
	private JRadioButton rdbtnF6;
	private JRadioButton rdbtnF7;
	private JLabel lblX;
	private JProgressBar progressBar;
	private boolean details = false;
	private boolean running = false;
	private JTextArea txtrDetails;
	private JScrollPane scrollPane;
	private JButton btnCalculateMinimum;
	private GAWorker worker;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					GUI window = new GUI();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public GUI() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 814, 432);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JPanel panel = new JPanel();
		frame.getContentPane().add(panel, BorderLayout.WEST);
		GridBagLayout gbl_panel = new GridBagLayout();
		gbl_panel.columnWidths = new int[]{0, 0};
		gbl_panel.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0};
		gbl_panel.columnWeights = new double[]{1.0, Double.MIN_VALUE};
		gbl_panel.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE};
		panel.setLayout(gbl_panel);
		
		JLabel lblFunction = new JLabel("Function:");
		GridBagConstraints gbc_lblFunction = new GridBagConstraints();
		gbc_lblFunction.insets = new Insets(0, 0, 5, 0);
		gbc_lblFunction.gridx = 0;
		gbc_lblFunction.gridy = 0;
		panel.add(lblFunction, gbc_lblFunction);
		
		rdbtnF3 = new JRadioButton("f3(x1,x2,x3,x4,x5) = (x1-p1)^2 + (x2-p2)^2 + ... + (x5-p5)^2");
		rdbtnF3.setSelected(true);
		buttonGroup.add(rdbtnF3);
		GridBagConstraints gbc_rdbtnF3 = new GridBagConstraints();
		gbc_rdbtnF3.anchor = GridBagConstraints.WEST;
		gbc_rdbtnF3.insets = new Insets(0, 0, 5, 0);
		gbc_rdbtnF3.gridx = 0;
		gbc_rdbtnF3.gridy = 1;
		panel.add(rdbtnF3, gbc_rdbtnF3);
		
		JPanel panel_4 = new JPanel();
		GridBagConstraints gbc_panel_4 = new GridBagConstraints();
		gbc_panel_4.insets = new Insets(0, 0, 5, 0);
		gbc_panel_4.fill = GridBagConstraints.BOTH;
		gbc_panel_4.gridx = 0;
		gbc_panel_4.gridy = 2;
		panel.add(panel_4, gbc_panel_4);
		
		JLabel lblArguments = new JLabel("Arguments (p):");
		panel_4.add(lblArguments);
		
		textField = new JTextField();
		textField.setText("20,30,0,80,-10");
		panel_4.add(textField);
		textField.setColumns(10);
		
		 rdbtnF4 = new JRadioButton("f4(x,y) = | (x-y) * (x+y) | + (x^2+y^2)^0.5");
		buttonGroup.add(rdbtnF4);
		GridBagConstraints gbc_rdbtnF4 = new GridBagConstraints();
		gbc_rdbtnF4.anchor = GridBagConstraints.WEST;
		gbc_rdbtnF4.insets = new Insets(0, 0, 5, 0);
		gbc_rdbtnF4.gridx = 0;
		gbc_rdbtnF4.gridy = 3;
		panel.add(rdbtnF4, gbc_rdbtnF4);
		
		rdbtnF6 = new JRadioButton("f6(x) = (0.5 - sin^2(sqrt(Sum(xi^2))) - 0.5) / (1.0 + 0.001 * Sum(xi^2))^2");
		buttonGroup.add(rdbtnF6);
		GridBagConstraints gbc_rdbtnF6 = new GridBagConstraints();
		gbc_rdbtnF6.anchor = GridBagConstraints.WEST;
		gbc_rdbtnF6.insets = new Insets(0, 0, 5, 0);
		gbc_rdbtnF6.gridx = 0;
		gbc_rdbtnF6.gridy = 4;
		panel.add(rdbtnF6, gbc_rdbtnF6);
		
		rdbtnF7 = new JRadioButton("f7(x) = (Sum(xi^2))^0.25 * [sin^2(50*(Sum(xi^2))^0.1) +1.0]");
		buttonGroup.add(rdbtnF7);
		GridBagConstraints gbc_rdbtnF7 = new GridBagConstraints();
		gbc_rdbtnF7.insets = new Insets(0, 0, 5, 0);
		gbc_rdbtnF7.anchor = GridBagConstraints.WEST;
		gbc_rdbtnF7.gridx = 0;
		gbc_rdbtnF7.gridy = 5;
		panel.add(rdbtnF7, gbc_rdbtnF7);
		
		JPanel panel_12 = new JPanel();
		GridBagConstraints gbc_panel_12 = new GridBagConstraints();
		gbc_panel_12.fill = GridBagConstraints.BOTH;
		gbc_panel_12.gridx = 0;
		gbc_panel_12.gridy = 6;
		panel.add(panel_12, gbc_panel_12);
		
		JLabel lblDimensionf = new JLabel("Dimension (f6 & f7):");
		panel_12.add(lblDimensionf);
		
		txtDimension = new JTextField();
		txtDimension.setText("5");
		panel_12.add(txtDimension);
		txtDimension.setColumns(10);
		
		JPanel panel_1 = new JPanel();
		frame.getContentPane().add(panel_1, BorderLayout.CENTER);
		GridBagLayout gbl_panel_1 = new GridBagLayout();
		gbl_panel_1.columnWidths = new int[]{0, 0};
		gbl_panel_1.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
		gbl_panel_1.columnWeights = new double[]{1.0, Double.MIN_VALUE};
		gbl_panel_1.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
		panel_1.setLayout(gbl_panel_1);
		
		JLabel lblParameters = new JLabel("Parameters:");
		GridBagConstraints gbc_lblParameters = new GridBagConstraints();
		gbc_lblParameters.insets = new Insets(0, 0, 5, 0);
		gbc_lblParameters.gridx = 0;
		gbc_lblParameters.gridy = 0;
		panel_1.add(lblParameters, gbc_lblParameters);
		
		JPanel panel_5 = new JPanel();
		GridBagConstraints gbc_panel_5 = new GridBagConstraints();
		gbc_panel_5.insets = new Insets(0, 0, 5, 0);
		gbc_panel_5.fill = GridBagConstraints.BOTH;
		gbc_panel_5.gridx = 0;
		gbc_panel_5.gridy = 1;
		panel_1.add(panel_5, gbc_panel_5);
		
		JLabel lblPopulazionSize = new JLabel("Populazion Size:");
		panel_5.add(lblPopulazionSize);
		
		txtPopsize = new JTextField();
		txtPopsize.setText("30");
		panel_5.add(txtPopsize);
		txtPopsize.setColumns(10);
		
		JPanel panel_6 = new JPanel();
		GridBagConstraints gbc_panel_6 = new GridBagConstraints();
		gbc_panel_6.insets = new Insets(0, 0, 5, 0);
		gbc_panel_6.fill = GridBagConstraints.BOTH;
		gbc_panel_6.gridx = 0;
		gbc_panel_6.gridy = 2;
		panel_1.add(panel_6, gbc_panel_6);
		
		JLabel lblNumberOfIterations = new JLabel("Number of Iterations:");
		panel_6.add(lblNumberOfIterations);
		
		txtMaxiterations = new JTextField();
		txtMaxiterations.setText("10000");
		panel_6.add(txtMaxiterations);
		txtMaxiterations.setColumns(10);
		
		JPanel panel_7 = new JPanel();
		GridBagConstraints gbc_panel_7 = new GridBagConstraints();
		gbc_panel_7.insets = new Insets(0, 0, 5, 0);
		gbc_panel_7.fill = GridBagConstraints.BOTH;
		gbc_panel_7.gridx = 0;
		gbc_panel_7.gridy = 3;
		panel_1.add(panel_7, gbc_panel_7);
		
		JLabel lblPercentageOfMutation = new JLabel("Percentage of Mutation:");
		panel_7.add(lblPercentageOfMutation);
		
		txtPmutation = new JTextField();
		txtPmutation.setText("0.008");
		panel_7.add(txtPmutation);
		txtPmutation.setColumns(10);
		
		final JPanel panel_13 = new JPanel();
		final JButton btnAdvanced = new JButton("Advanced");
		btnAdvanced.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				panel_13.setVisible(true);
				btnAdvanced.setVisible(false);
				simple = false;
			}
		});
		GridBagConstraints gbc_btnAdvanced = new GridBagConstraints();
		gbc_btnAdvanced.insets = new Insets(0, 0, 5, 0);
		gbc_btnAdvanced.gridx = 0;
		gbc_btnAdvanced.gridy = 4;
		panel_1.add(btnAdvanced, gbc_btnAdvanced);
		
		
		panel_13.setVisible(false);
		GridBagConstraints gbc_panel_13 = new GridBagConstraints();
		gbc_panel_13.gridheight = 2;
		gbc_panel_13.insets = new Insets(0, 0, 5, 0);
		gbc_panel_13.fill = GridBagConstraints.BOTH;
		gbc_panel_13.gridx = 0;
		gbc_panel_13.gridy = 5;
		panel_1.add(panel_13, gbc_panel_13);
		GridBagLayout gbl_panel_13 = new GridBagLayout();
		gbl_panel_13.columnWidths = new int[]{0, 0};
		gbl_panel_13.rowHeights = new int[]{0, 0, 0, 0, 0, 0};
		gbl_panel_13.columnWeights = new double[]{1.0, Double.MIN_VALUE};
		gbl_panel_13.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
		panel_13.setLayout(gbl_panel_13);
		
		JPanel panel_8 = new JPanel();
		GridBagConstraints gbc_panel_8 = new GridBagConstraints();
		gbc_panel_8.fill = GridBagConstraints.BOTH;
		gbc_panel_8.insets = new Insets(0, 0, 5, 0);
		gbc_panel_8.gridx = 0;
		gbc_panel_8.gridy = 0;
		panel_13.add(panel_8, gbc_panel_8);
		
		JLabel lblUpperLimit = new JLabel("Upper Limit:");
		panel_8.add(lblUpperLimit);
		
		txtUpperlimit = new JTextField();
		txtUpperlimit.setText("100");
		panel_8.add(txtUpperlimit);
		txtUpperlimit.setColumns(10);
		
		JPanel panel_9 = new JPanel();
		GridBagConstraints gbc_panel_9 = new GridBagConstraints();
		gbc_panel_9.fill = GridBagConstraints.BOTH;
		gbc_panel_9.insets = new Insets(0, 0, 5, 0);
		gbc_panel_9.gridx = 0;
		gbc_panel_9.gridy = 1;
		panel_13.add(panel_9, gbc_panel_9);
		
		JLabel lblLowerLimit = new JLabel("Lower Limit:");
		panel_9.add(lblLowerLimit);
		
		txtLowerlimit = new JTextField();
		txtLowerlimit.setText("-100");
		txtLowerlimit.setColumns(10);
		panel_9.add(txtLowerlimit);
		
		JPanel panel_10 = new JPanel();
		GridBagConstraints gbc_panel_10 = new GridBagConstraints();
		gbc_panel_10.fill = GridBagConstraints.BOTH;
		gbc_panel_10.insets = new Insets(0, 0, 5, 0);
		gbc_panel_10.gridx = 0;
		gbc_panel_10.gridy = 2;
		panel_13.add(panel_10, gbc_panel_10);
		
		JLabel lblPrecision = new JLabel("Precision:");
		panel_10.add(lblPrecision);
		
		txtPrecision = new JTextField();
		txtPrecision.setText("3");
		txtPrecision.setColumns(10);
		panel_10.add(txtPrecision);
		
		JButton btnCalculateNumberOf = new JButton("Calculate Number of Bits");
		btnCalculateNumberOf.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				int ul = Integer.parseInt(txtUpperlimit.getText());
				int ll = Integer.parseInt(txtLowerlimit.getText());
				int p = Integer.parseInt(txtPrecision.getText());
				double n = Math.log((ul - ll)*Math.pow(10, p) + 1) / Math.log(2);
				txtNumbits.setText(String.valueOf((int)n + 3));
			}
		});
		panel_10.add(btnCalculateNumberOf);
		
		JPanel panel_11 = new JPanel();
		GridBagConstraints gbc_panel_11 = new GridBagConstraints();
		gbc_panel_11.fill = GridBagConstraints.BOTH;
		gbc_panel_11.insets = new Insets(0, 0, 5, 0);
		gbc_panel_11.gridx = 0;
		gbc_panel_11.gridy = 3;
		panel_13.add(panel_11, gbc_panel_11);
		
		JLabel lblNumberOfBits = new JLabel("Number of Bits:");
		panel_11.add(lblNumberOfBits);
		
		txtNumbits = new JTextField();
		txtNumbits.setText("20");
		txtNumbits.setColumns(10);
		panel_11.add(txtNumbits);
		
		JButton btnSimple = new JButton("Simple");
		btnSimple.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				panel_13.setVisible(false);
				btnAdvanced.setVisible(true);
				simple = true;
			}
		});
		GridBagConstraints gbc_btnSimple = new GridBagConstraints();
		gbc_btnSimple.gridx = 0;
		gbc_btnSimple.gridy = 4;
		panel_13.add(btnSimple, gbc_btnSimple);
		
		JPanel panel_3 = new JPanel();
		frame.getContentPane().add(panel_3, BorderLayout.SOUTH);
		panel_3.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_2 = new JPanel();
		panel_3.add(panel_2, BorderLayout.NORTH);
		GridBagLayout gbl_panel_2 = new GridBagLayout();
		gbl_panel_2.columnWidths = new int[]{173, 119, 105, 42, 20, 146, 0};
		gbl_panel_2.rowHeights = new int[]{23, 0};
		gbl_panel_2.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE};
		gbl_panel_2.rowWeights = new double[]{0.0, Double.MIN_VALUE};
		panel_2.setLayout(gbl_panel_2);
		
		btnCalculateMinimum = new JButton("Calculate minimum");
		GridBagConstraints gbc_btnCalculateMinimum = new GridBagConstraints();
		gbc_btnCalculateMinimum.anchor = GridBagConstraints.NORTH;
		gbc_btnCalculateMinimum.insets = new Insets(0, 0, 0, 5);
		gbc_btnCalculateMinimum.gridx = 0;
		gbc_btnCalculateMinimum.gridy = 0;
		panel_2.add(btnCalculateMinimum, gbc_btnCalculateMinimum);
		
		JLabel lblSolution = new JLabel("Solution:");
		GridBagConstraints gbc_lblSolution = new GridBagConstraints();
		gbc_lblSolution.anchor = GridBagConstraints.EAST;
		gbc_lblSolution.insets = new Insets(0, 0, 0, 5);
		gbc_lblSolution.gridx = 1;
		gbc_lblSolution.gridy = 0;
		panel_2.add(lblSolution, gbc_lblSolution);
		
		lblX = new JLabel("X = ");
		GridBagConstraints gbc_lblX = new GridBagConstraints();
		gbc_lblX.anchor = GridBagConstraints.WEST;
		gbc_lblX.insets = new Insets(0, 0, 0, 5);
		gbc_lblX.gridx = 2;
		gbc_lblX.gridy = 0;
		panel_2.add(lblX, gbc_lblX);
		
		progressBar = new JProgressBar();
		GridBagConstraints gbc_progressBar = new GridBagConstraints();
		gbc_progressBar.insets = new Insets(0, 0, 0, 5);
		gbc_progressBar.gridx = 4;
		gbc_progressBar.gridy = 0;
		panel_2.add(progressBar, gbc_progressBar);
		
		final JButton btnDetails = new JButton("Details >>");
		btnDetails.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Dimension dimension = frame.getSize();
				if(!details){
					scrollPane.setVisible(true);
					btnDetails.setText("Details <<");
					frame.setSize(new Dimension(dimension.width,dimension.height+260));
					details = true;
				}
				else{
					scrollPane.setVisible(false);
					btnDetails.setText("Details >>");
					frame.setSize(new Dimension(dimension.width,dimension.height-260));
					details = false;
				}
			}
		});
		GridBagConstraints gbc_btnDetails = new GridBagConstraints();
		gbc_btnDetails.gridx = 5;
		gbc_btnDetails.gridy = 0;
		panel_2.add(btnDetails, gbc_btnDetails);
		
		
		
		txtrDetails = new JTextArea();
		txtrDetails.setEditable(false);
		txtrDetails.setRows(15);
		
		scrollPane = new JScrollPane(txtrDetails);
		scrollPane.setVisible(false);
		scrollPane.setBorder(BorderFactory.createLineBorder(Color.black, 2));
		panel_3.add(scrollPane, BorderLayout.CENTER);
		
		btnCalculateMinimum.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if(!running){
					runGA();
					btnCalculateMinimum.setText("Stop");
					running = true;
				}
				else{
					worker.cancel(true);
					txtrDetails.append("Canceled");
					lblX.setText("Canceled");
				}
			}
		});
	}

	protected void runGA() {
		int f = 0;
		if(rdbtnF3.isSelected())
			f=3;
		else if(rdbtnF4.isSelected())
			f=4;
		else if(rdbtnF6.isSelected())
			f=6;
		else if(rdbtnF7.isSelected())
			f=7;
		int popSize = Integer.parseInt(txtPopsize.getText());
		float pMutation = Float.parseFloat(txtPmutation.getText());
		int maxIteration = Integer.parseInt(txtMaxiterations.getText());
		int dimension = Integer.parseInt(txtDimension.getText());
		int lowerLimit = Integer.parseInt(txtLowerlimit.getText());
		int upperLimit = Integer.parseInt(txtUpperlimit.getText());
		int precision = Integer.parseInt(txtPrecision.getText());
		int numBits = Integer.parseInt(txtNumbits.getText());
		GA ga;
		if(simple)
			ga = new GA(popSize, pMutation, maxIteration, f, dimension);
		else
			ga = new GA(popSize, pMutation, maxIteration, dimension, precision, upperLimit, lowerLimit, numBits, f);
		progressBar.setValue(0);
		progressBar.setStringPainted(true);
		
		txtrDetails.setText("");
		lblX.setText("");
		worker = new GAWorker(ga);
		worker.addPropertyChangeListener(this);
		worker.execute();
	}
	
	public void propertyChange(PropertyChangeEvent evt) {
        if ("progress" == evt.getPropertyName()) {
            int progress = (Integer) evt.getNewValue();
            progressBar.setValue(progress);
        } 
    }
	
	class GAWorker extends SwingWorker<String, Void>{

		GA ga;
		public GAWorker(GA ga) {
			this.ga = ga;
		}
		
		@Override
		protected String doInBackground() throws Exception {
			ga.createPop();
			ga.evalPop();
			
			int[] rand = new int[3];
			String[] child = new String[ga.getDimension()];
			int i = 0;
			int progress=0;
			int pro;
			boolean contains;
			do{
				if(isCancelled())
					return null;
				rand = ga.get3Random();
				ga.worstOf3(rand);
				contains = false;
				while(!contains){
					child = ga.cross(rand[1],rand[2]);
					ga.mutate(child);
					if(!ga.compare(child))
						contains = true;
				}
				ga.getIndividuals().set(rand[0], child);
				ga.getValues().set(rand[0], ga.evalF(child));
				pro = (int)((double)i/(double)ga.getMaxIteration() * 100);
				if(pro > progress){
					progress = pro;
					setProgress(progress);
				}
				if(i%100 == 0 && i<= 1000) txtrDetails.append(ga.printMin(i));
				if(i%1000 == 0 && i>1000) txtrDetails.append(ga.printMin(i));
			}while(++i<ga.getMaxIteration());
			txtrDetails.append(ga.printMin(i));
			int min = ga.findMinimum();
			return ga.printDec(min);
		}
		
		@Override
		protected void done() {
			try {
				if(!isCancelled()){
					setProgress(100);
					lblX.setText(get());
					txtrDetails.append("done");
				}
				running = false;
				btnCalculateMinimum.setText("Calculate Minimum");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		
	}
}
